Knight dialer [Matrix Exponentiation]¶
Time: O(LogN); Space: O(1); medium
The chess knight has a unique movement, it may move two squares vertically and one square horizontally, or two squares horizontally and one square vertically (with both forming the shape of an L). The possible movements of chess knight are shown in this diagaram:
A chess knight can move as indicated in the chess diagram below:
We have a chess knight and a phone pad as shown below, the knight can only stand on a numeric cell (i.e. blue cell).
Given an integer n, return how many distinct phone numbers of length n we can dial.
You are allowed to place the knight on any numeric cell initially and then you should perform n - 1 jumps to dial a number of length n. All jumps should be valid knight jumps.
As the answer may be very large, return the answer modulo 10^9 + 7.
Example 1:
Input: n = 1
Output: 10
Explanation:
We need to dial a number of length 1, so placing the knight over any numeric cell of the 10 cells is sufficient.
Example 2:
Input: n = 2
Output: 20
Explanation:
All the valid number we can dial are [04, 06, 16, 18, 27, 29, 34, 38, 40, 43, 49, 60, 61, 67, 72, 76, 81, 83, 92, 94]
Example 3:
Input: n = 3
Output: 46
Example 4:
Input: n = 4
Output: 104
Example 5:
Input: n = 3131
Output: 136006598
Explanation:
Please take care of the mod.
Constraints:
1 <= n <= 5000
1. Dynamic Programming [O(N), O(1)]¶
Intuition
Let f(start, n) be the number of ways to dial an n digit number, where the knight starts at square start. We can create a recursion, writing this in terms of f(x, n-1)’s.
Algorithm
By hand or otherwise, have a way to query what moves are available at each square. This implies the exact recursion for f. For example, from 1 we can move to 6, 8, so f(1, n) = f(6, n-1) + f(8, n-1).
After, let’s keep track of dp[start] = f(start, n), and update it for each n from 1, 2, …, N.
At the end, the answer is f(0, N) + f(1, N) + … + f(9, N) = sum(dp).
[7]:
class Solution1(object):
"""
Time: O(N)
Space: O(1)
"""
def knightDialer(self, N):
"""
:type N: int
:rtype: int
"""
MOD = 10**9 + 7
moves = [[4, 6], [6, 8], [7, 9], [4, 8], [3, 9, 0], [],
[1, 7, 0], [2, 6], [1, 3], [2, 4]]
dp = [1] * 10
for hops in range(N-1):
dp2 = [0] * 10
for node, count in enumerate(dp):
for nei in moves[node]:
dp2[nei] += count
dp2[nei] %= MOD
dp = dp2
return sum(dp) % MOD
[8]:
s = Solution1()
N = 1
assert s.knightDialer(N) == 10
N = 2
assert s.knightDialer(N) == 20
N = 3
assert s.knightDialer(N) == 46
N = 4
assert s.knightDialer(N) == 104
N = 3131
assert s.knightDialer(N) == 136006598
2. Dynamic Programming [O(N), O(1)]¶
[9]:
class Solution2(object):
def knightDialer(self, N):
"""
:type N: int
:rtype: int
"""
MOD = 10**9 + 7
moves = [[4, 6], [6, 8], [7, 9], [4, 8], [3, 9, 0], [],
[1, 7, 0], [2, 6], [1, 3], [2, 4]]
dp = [[1 for _ in range(10)] for _ in range(2)]
for i in range(N-1):
dp[(i+1) % 2] = [0] * 10
for j in range(10):
for nei in moves[j]:
dp[(i+1) % 2][nei] += dp[i % 2][j]
dp[(i+1) % 2][nei] %= MOD
return sum(dp[(N-1) % 2]) % MOD
[10]:
s = Solution2()
N = 1
assert s.knightDialer(N) == 10
N = 2
assert s.knightDialer(N) == 20
N = 3
assert s.knightDialer(N) == 46
N = 4
assert s.knightDialer(N) == 104
N = 3131
assert s.knightDialer(N) == 136006598
3. Dynamic Programming [O(LogN), O(1)]¶
[11]:
class Solution3(object):
"""
Time: O(LogN)
Space: O(1)
"""
def knightDialer(self, N):
"""
:type N: int
:rtype: int
"""
def matrix_expo(A, K):
result = [[int(i==j) for j in range(len(A))]
for i in range(len(A))]
while K:
if K % 2:
result = matrix_mult(result, A)
A = matrix_mult(A, A)
K //= 2
return result
def matrix_mult(m1, m2):
return [[sum(a * b for a, b in zip(m1_row, m2_col)) %MOD for m2_col in zip(*m2)] for m1_row in m1]
MOD = 10**9 + 7
T = [[0, 0, 0, 0, 1, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[0, 0, 0, 0, 1, 0, 0, 0, 1, 0],
[1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
[0, 1, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 1, 0, 0, 0, 0, 0]]
return sum(map(sum, matrix_expo(T, N-1))) % MOD
[12]:
s = Solution3()
N = 1
assert s.knightDialer(N) == 10
N = 2
assert s.knightDialer(N) == 20
N = 3
assert s.knightDialer(N) == 46
N = 4
assert s.knightDialer(N) == 104
N = 3131
assert s.knightDialer(N) == 136006598